package top.kaoshanji.p2ch08a;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;

/**
 * 代码清单-p2ch08a-05-253页a：使用Java编译器API的表达式求值方式
 * author kaoshanji
 * ^_^
 * create time 2025/1/22 15:57
 */
public class JavaCompilerExample02 extends ClassLoader {

    private static final Logger logger = LogManager.getLogger(JavaCompilerExample02.class);

    public static void main(String[] args) throws Exception {
        logger.info("...代码清单-p2ch08a-05-253页a：使用Java编译器API的表达式求值方式...");

        String expr = "(3+4)*7-1.2*3";
        JavaCompilerExample02 example = new JavaCompilerExample02();
        double value = example.calculate(expr);

        logger.info("...value:{}...", value);
    }

    public double calculate(String expr) throws Exception {

        String className = "CalculatorMain";
        String methodName = "calculate";
        String source = "public class " + className + " { public static double " + methodName
                + "() { return " + expr + "; } }";

        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
        StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
        JavaFileObject sourceObject = new StringSourceJavaFileObject(className, source);
        Iterable<? extends JavaFileObject> fileObjects = Arrays.asList(sourceObject);

        Path output = Files.createTempDirectory("calculator");
        Iterable<String> options = Arrays.asList("-d", output.toString());
        JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, options, null, fileObjects);
        boolean result = task.call();

        if (result) {
            byte[] classData = Files.readAllBytes(Paths.get(output.toString(), className + ".class"));
            Class<?> clazz = defineClass(className, classData, 0, classData.length);
            Method method = clazz.getMethod(methodName);
            Object value = method.invoke(null);
            return (Double)value;
        } else {
            throw new Exception("无法识别的表达式。");
        }

    }


}
